/*
 * Copyright (c) 2025 Airbyte, Inc., all rights reserved.
 */

package io.airbyte.integrations.source.mysql

import java.math.BigInteger

/**
 * Converts a list of integers to a BigInteger using the specified base. This treats the list as
 * digits in a positional numeral system.
 */
private fun intListToBigInt(values: List<Int>, base: BigInteger): BigInteger {
    var result = BigInteger.ZERO
    for (value in values) {
        result = result.multiply(base).add(BigInteger.valueOf(value.toLong()))
    }
    return result
}

/**
 * Converts a BigInteger back to a list of integers of the specified length using the specified
 * base. This is the inverse operation of intListToBigInt.
 */
private fun bigIntToIntList(num: BigInteger, length: Int, base: BigInteger): List<Int> {
    var n = num
    val result = MutableList(length) { 0 }
    for (i in (length - 1) downTo 0) {
        val rem = n.mod(base).toInt()
        result[i] = rem
        n = n.divide(base)
    }
    return result
}

/**
 * Generates interpolated strings between start and end using Unicode code points. The strings are
 * generated by treating them as numbers in a base-(Unicode max) system.
 */
fun unicodeInterpolatedStrings(start: String, end: String, steps: Int): List<String> {
    fun toCodePoints(s: String): List<Int> {
        val codePoints = mutableListOf<Int>()
        var i = 0
        while (i < s.length) {
            val cp = s.codePointAt(i)
            codePoints.add(cp)
            i += Character.charCount(cp)
        }
        return codePoints
    }

    fun fromCodePoints(codePoints: List<Int>): String {
        val sb = StringBuilder()
        for (cp in codePoints) {
            sb.append(Character.toChars(cp))
        }
        return sb.toString()
    }

    val base = BigInteger.valueOf(0x110000L) // Max Unicode code point + 1
    val a = toCodePoints(start)
    val b = toCodePoints(end)
    val maxLen = maxOf(a.size, b.size)
    val aPadded = a + List(maxLen - a.size) { 0 }
    val bPadded = b + List(maxLen - b.size) { 0 }

    val aNum = intListToBigInt(aPadded, base)
    val bNum = intListToBigInt(bPadded, base)

    val result = mutableListOf<String>()
    for (i in 0..steps) {
        val mid =
            if (steps == 0) {
                aNum
            } else {
                val fraction =
                    BigInteger.valueOf(i.toLong())
                        .multiply(bNum - aNum)
                        .divide(BigInteger.valueOf(steps.toLong()))
                aNum + fraction
            }
        val cps = bigIntToIntList(mid, maxLen, base)
        result.add(
            when (i) {
                0 -> start
                steps -> end
                else -> fromCodePoints(cps)
            }
        )
    }

    return result
}

/**
 * Generates interpolated strings between start and end using GUID-legal characters. This is
 * optimized for GUID/UUID strings which only use hexadecimal and hyphen characters.
 */
fun guidInterpolatedStrings(start: String, end: String, steps: Int): List<String> {
    // GUID-legal characters: 0-9, A-F, a-f, and hyphen (case-sensitive, ASCII order)
    val guidChars = "-0123456789ABCDEFabcdef"

    fun charToIndex(c: Char): Int {
        val idx = guidChars.indexOf(c)
        if (idx == -1) {
            throw IllegalArgumentException("Character '$c' is not GUID-legal")
        }
        return idx
    }

    fun indexToChar(idx: Int): Char {
        if (idx < 0 || idx >= guidChars.length) {
            throw IllegalArgumentException("Index $idx out of range for GUID characters")
        }
        return guidChars[idx]
    }

    fun toIndices(s: String): List<Int> {
        return s.map { charToIndex(it) }
    }

    fun fromIndices(indices: List<Int>): String {
        return indices.map { indexToChar(it) }.joinToString("")
    }

    val base =
        BigInteger.valueOf(guidChars.length.toLong()) // 23 valid GUID characters (case-sensitive)
    val a = toIndices(start)
    val b = toIndices(end)
    val maxLen = maxOf(a.size, b.size)
    val aPadded = a + List(maxLen - a.size) { 0 }
    val bPadded = b + List(maxLen - b.size) { 0 }

    val aNum = intListToBigInt(aPadded, base)
    val bNum = intListToBigInt(bPadded, base)

    val result = mutableListOf<String>()
    for (i in 0..steps) {
        val mid =
            if (steps == 0) {
                aNum
            } else {
                val fraction =
                    BigInteger.valueOf(i.toLong())
                        .multiply(bNum - aNum)
                        .divide(BigInteger.valueOf(steps.toLong()))
                aNum + fraction
            }
        val indices = bigIntToIntList(mid, maxLen, base)
        result.add(fromIndices(indices))
    }

    return result
}
